#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"

#include <sys/epoll.h>

const int Channel::kNoneEvent = 0;  // fd不对任何事件感兴趣
//EPOLLPRI表示优先级较高的事件可读
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;  // fd对读事件感兴趣
const int Channel::kWriteEvent = EPOLLOUT; // fd对写事件感兴趣

Channel::Channel(EventLoop* loop,int fd)
    :loop_(loop)
    ,fd_(fd)
    ,events_(0)
    ,revents_(0)
    ,index_(-1)
    ,tied_(false)
{}

Channel::~Channel() {}

//channel的tie方法什么时候调用过？ 一个TcpConnection新连接创建的时候 TcpConnection => Channel
//这个方法可以让channel跟踪TcpConnection的存货状态
void Channel::tie(const std::shared_ptr<void>& obj)
{
    tie_ = obj;
    tied_ = true;
}

//当改变channel所表示的events事件后，update负责在poller里面更改fd相应的事件epoll_ctl,要通过EventLoop作为中介实现
//这是因为channel中并没有poller
void Channel::update()
{
    loop_->updateChannel(this);
}

//在channel所属的EventLoop中，把当前的channel删除
void Channel::remove()
{
    loop_->removeChannel(this);
}

//fd得到poller通知以后处理事件(调用回调函数处理)
void Channel::handleEvent(Timestamp receivTime)
{
    //如果绑定过，也就是说tie_监听过channel
    //如果tie_为空，说明TcpConnection已经没有了
    if(tied_)
    {
        //将弱指针转换为强指针
        //使用weak_ptr成员lock，则可返回其指向内存的一个shared_ptr对象，
        //且在所指对象内存已经无效时，返回指针空值（nullptr）。
        //由于weak_ptr是指向shared_ptr所指向的内存的，所以，weak_ptr并不能独立存在。
        std::shared_ptr<void> guard = tie_.lock();
        //转换成功,channel还存活
        if(guard)
        {
            handleEventWithGuard(receivTime);
        }
    }
    else
    {
        handleEventWithGuard(receivTime);
    }
}

void Channel::handleEventWithGuard(Timestamp receivTime)
{
    LOG_INFO("Channel handleEvent revents:%d\n",revents_);
    //如果一个文件描述符（socket或其他类型）被挂断，即对方关闭了连接，那么 epoll 会报告 EPOLLHUP 事件。
    if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN))
    {
        if(closeCallback_){
            closeCallback_();
        }
    }

    if(revents_ & EPOLLERR)
    {
        if(errorCallback_)
        {
            errorCallback_();
        }
    }

    if(revents_ & (EPOLLIN |EPOLLPRI))
    {
        if(readCallback_){
            readCallback_(receivTime);
        }
    }

    if (revents_ & EPOLLOUT)
    {
        if (writeCallback_)
        {
            writeCallback_();
        }
    }
}
